/**
 * $RCSfile: EmailGateway.java,v $
 * $Revision: 1.19 $
 * $Date: 2001/09/09 15:42:40 $
 *
 * Copyright (C) 1999-2001 CoolServlets Inc. All rights reserved.
 * ===================================================================
 * The Jive Software License (based on Apache Software License, Version 1.1)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by
 *        Jive Software (http://www.jivesoftware.com)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Jive" and "CoolServlets" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please
 *    contact webmaster@coolservlets.com.
 *
 * 5. Products derived from this software may not be called "Jive",
 *    nor may "Jive" appear in their name, without prior written
 *    permission of CoolServlets.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL COOLSERVLETS INC OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 * This software consists of voluntary contributions made by many
 * individuals on behalf of Jive Software. For more information
 * on Jive Software please visit http://www.jivesoftware.com.
 */

package com.jivesoftware.forum.gateway;

import java.io.*;
import java.util.*;
import java.text.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import com.jivesoftware.forum.*;
import com.jivesoftware.forum.model.ForumMessageNotFoundException;
import com.jivesoftware.util.StringUtils;

/**
 * A gateway to synchronize a forum with the contents of an email account or
 * a mailing list. For example, say you want to sychronize with the mailing
 * list <tt>mail-list@example.com</tt>. To do so, you would set the "to address"
 * to be <tt>mail-list@example.com</tt>. For importing, you'd need to create and
 * use a POP3 account that was subscribed to the list, for example
 * <tt>mail-list-robot@example.com</tt>.<p>
 *
 * The following properties must be set with valid values before importing and
 * exporting can work: <ul>
 *      <li> <tt>POP3Host</tt>
 *      <li> <tt>POP3Username</tt>
 *      <li> <tt>POP3Password</tt>
 *      <li> <tt>SMTPHost</tt>
 *      <li> <tt>defaultFromAddress</tt>
 *      <li> <tt>toAddress</tt> </ul>
 *
 * All other properties start with reasonable default values, but can be
 * changed as necessary.
 *
 * @author Bruce Ritchie
 */
public class EmailGateway implements Gateway {

    private POP3Gateway gateway;
    private Session SMTPSession = null;

    private String SMTPHost = null;
    private int SMTPPort = 25;
    private String SMTPUsername = null;
    private String SMTPPassword = null;

    private String toAddress = null;
    private String defaultFromAddress = null;

    private boolean emailPrefEnabled = true;
    private boolean debugEnabled = false;

    /**
     * Used to flag messages in the forum with a message id
     * Specific to this gateway
     */
    public static final String GATEWAY_MESSAGE_ID = "Email-Message-ID";

    /**
     * Used to flag messages in the forum with a parent id
     * Specific to this gateway
     */
    public static final String GATEWAY_PARENT_ID = "Email-Parent-ID";

    /**
     * Create a new EmailGateway instance.
     */
    public EmailGateway(ForumFactory factory, Forum forum) {
        // Configure POP Gateway
        gateway = new POP3Gateway(factory, forum);
    }

    //FROM THE GATEWAY INTERFACE//

    public synchronized void importData(Date afterDate) throws GatewayException
    {
        gateway.importData(afterDate);
    }

    public synchronized void exportData(ForumMessage forumMessage)
            throws GatewayException
    {
        if (SMTPHost == null || toAddress == null || defaultFromAddress == null)
        {
            throw new GatewayException("Required properties are not all set.");
        }

        // Refuse to re-export a message which has previously been exported
        // or imported or which was automatically created for threading
        // purposes by an import gateway.
        if (forumMessage.getProperty(GATEWAY_MESSAGE_ID) != null ||
                forumMessage.getProperty(gateway.DUMMY_PARENT_HEADER) != null) {
            return;
        }

        try {
            // Create the mail session if necessary.
            if (SMTPSession == null) {
                Properties mailProps = new Properties();
                mailProps.setProperty("mail.smtp.host", SMTPHost);
                mailProps.setProperty("mail.smtp.port", String.valueOf(SMTPPort));
                mailProps.setProperty("mail.debug", String.valueOf(debugEnabled));
                SMTPSession = Session.getInstance(mailProps, null);
            }

            // Next, determine the "from" address, which will normally be
            // the message author's name and email address. However, we have to
            // use a default address in some circumstances when that info isn't
            // available.
            InternetAddress fromAddress = null;
            if (!forumMessage.isAnonymous()) {
                String user = forumMessage.getUser();
                Map<String, String> userInfo = ForumFactory.getUserInfoCenter().getUerInfo(user);
                String name, email = null;
                // Get the name, or use their username if the name is hidden.
                if (Boolean.parseBoolean(userInfo.get(UserInfoCenter.NAME_VISIABLE))) {
                    name = userInfo.get(UserInfoCenter.NAME);
                }
                else {
                    name = userInfo.get(UserInfoCenter.USER_NAME);
                }
                // Get their email address, or use the dummy address if their
                // email is hidden and the admin hasn't overriden that.
                if (!Boolean.parseBoolean(UserInfoCenter.EMAIL_VISIABLE) && emailPrefEnabled) {
                    email = defaultFromAddress;
                }
                else {
                    email = userInfo.get(UserInfoCenter.EMAIL);
                }
                fromAddress = new InternetAddress(email, name);
            }
            // Message was posted anonymously. However, a name and email might
            // be defined through extended properties.
            else if (forumMessage.getProperty("name") != null &&
                     forumMessage.getProperty("email") != null)
            {
                String name  = forumMessage.getProperty("name");
                String email = forumMessage.getProperty("email");
                fromAddress = new InternetAddress(email, name);
            }
            // Getting "from" address failed. Use the default address.
            else {
                fromAddress = new InternetAddress(defaultFromAddress);
            }

            // Create a new JavaMail message and set basic values on it.
            MimeMessage message = new MimeMessage(SMTPSession);
            message.setRecipient(Message.RecipientType.TO,
                    new InternetAddress(toAddress));
            message.setFrom(fromAddress);
            // The body is the message + the footer.
            StringBuffer body = new StringBuffer(forumMessage.getUnfilteredBody());
            Forum forum = gateway.factory.getForum(gateway.forumID);
            GatewayManager manager = forum.getGatewayManager();
            body.append(manager.getTranslatedFooter(forumMessage));
            message.setContent(body.toString(),"text/plain");

            message.setSubject(forumMessage.getSubject());

            // Get a parent message ID if one exists so we can set
            // the references header properly for threading to work.
            try {
                ForumThread thread = forumMessage.getForumThread();
                TreeWalker tree = thread.treeWalker();
                ForumMessage parent = tree.getParent(forumMessage);
                String parentId = parent.getProperty(GATEWAY_MESSAGE_ID);

                if (parentId != null && !parentId.equals("")) {
                    message.setHeader("In-Reply-To", parentId);
                }
            }
            catch (ForumMessageNotFoundException fmnfe) { /* ignore */ }

            // Send the message
            URLName url =
                new URLName("smtp", SMTPHost, SMTPPort, "", SMTPUsername, SMTPPassword);
            Transport trans =
                (Transport) new com.sun.mail.smtp.SMTPTransport(SMTPSession, url);
            trans.connect(SMTPHost, SMTPPort, SMTPUsername, SMTPPassword);
            trans.sendMessage(message, message.getRecipients(MimeMessage.RecipientType.TO));

            // Set the message ID so that we don't inadvertently cause a mail
            // loop to happen.
            forumMessage.setProperty(GATEWAY_MESSAGE_ID, message.getMessageID());
        }
        catch (Exception e) {
            throw new GatewayException(e);
        }
    }

    //GATEWAY PROPERTIES//

    /**
     * Returns the POP3 host (e.g. mail.example.com). The host is null by
     * default, but must be set before gateway imports can execute.
     *
     * @return the POP3 host.
     */
    public String getPOP3Host() {
        return gateway.getHost();
    }

    /**
     * Sets the POP3 host (e.g. mail.example.com). The host is null by
     * default, but must be set before gateway imports can execute.
     *
     * @param host the POP3 host.
     */
    public void setPOP3Host(String host) {
        gateway.setHost(host);
    }

    /**
     * Returns the port number that will be used when connecting to the POP3
     * server. The default is 110, the standard POP3 port number.
     *
     * @return the POP3 port number.
     */
    public int getPOP3Port() {
        return gateway.getPort();
    }

    /**
     * Sets the port number that will be used when connecting to the POP3
     * server. The default is 110, the standard POP3 port number.
     *
     * @param port the POP3 port number.
     */
    public synchronized void setPOP3Port(int port) {
        gateway.setPort(port);
    }

    /**
     * Returns the username that will be used when connection to the POP3 server.
     * By default the value is null, which means that no username will be used.
     *
     * @return the POP3 username.
     */
    public String getPOP3Username() {
        return gateway.getUsername();
    }

    /**
     * Sets the username that will be used when connecting to the POP3 server.
     * By default the value is null, which means that no username will be used.
     *
     * @param username the POP3 username.
     */
    public void setPOP3Username(String username) {
        gateway.setUsername(username);
    }

    /**
     * Returns the password that will be used when connection to the POP3 server.
     * By default the value is null, which means that no password will be used.
     *
     * @return the POP3 password.
     */
    public String getPOP3Password() {
        return gateway.getPassword();
    }

    /**
     * Sets the password that will be used when connecting to the POP3 server.
     * By default the value is null, which means that no password will be used.
     *
     * @param password the POP3 password.
     */
    public void setPOP3Password(String password) {
        gateway.setPassword(password);
    }

    /**
     * Returns true if the POP3 delete flag is set. When true, messages will be
     * deleted from the server after being downloaded. The default value is
     * false.
     *
     * @return true if messages will be deleted from the POP3 server after
     *      being downloaded.
     */
    public boolean isPOP3DeleteEnabled() {
        return gateway.isDeleteEnabled();
    }

    /**
     * Toggles the POP3 delete flag. When true, messages will be
     * deleted from the server after being downloaded. The default value is
     * false.
     *
     * @param POP3DeleteEnabled true if messages should be deleted from the
     *      POP3 server after being downloaded.
     */
    public void setPOP3DeleteEnabled(boolean POP3DeleteEnabled) {
        gateway.setDeleteEnabled(POP3DeleteEnabled);
    }

    /**
     * Returns the SMTP host (e.g. mail.example.com). The host is null by
     * default, but must be set before gateway exports can execute.
     *
     * @return the SMTP host.
     */
    public String getSMTPHost() {
        return SMTPHost;
    }

    /**
     * Sets the SMTP host (e.g. mail.example.com). The host is null by
     * default, but must be set before gateway exports can execute.
     *
     * @param host the SMTP host.
     */
    public synchronized void setSMTPHost(String host) {
        this.SMTPHost = host;
        // JavaMail Session no longer valid so set to null; it will get
        // recreated as needed.
        SMTPSession = null;
    }

    /**
     * Returns the port number that will be used when connecting to the SMTP
     * server. The default is 25, the standard SMTP port number.
     *
     * @return the SMTP port number.
     */
    public int getSMTPPort() {
        return SMTPPort;
    }

    /**
     * Sets the port number that will be used when connecting to the SMTP
     * server. The default is 25, the standard SMTP port number.
     *
     * @param port the SMTP port number.
     */
    public synchronized void setSMTPPort(int port) {
        this.SMTPPort = port;
        // JavaMail Session no longer valid so set to null; it will get
        // recreated as needed.
        SMTPSession = null;
    }

    /**
     * Returns the username that will be used when connecting to the SMTP
     * server. The default is null, or no username.
     *
     * @return the SMTP username.
     */
    public String getSMTPUsername() {
        return SMTPUsername;
    }

    /**
     * Sets the username that will be used when connecting to the SMTP
     * server. The default is null, or no username.
     *
     * @param port the SMTP username.
     */
    public synchronized void setSMTPUsername(String username) {
        this.SMTPUsername = username;
        // JavaMail Session no longer valid so set to null; it will get
        // recreated as needed.
        SMTPSession = null;
    }

    /**
     * Returns the password that will be used when connecting to the SMTP
     * server. The default is null, or no password.
     *
     * @return the SMTP password.
     */
    public String getSMTPPassword() {
        return SMTPPassword;
    }

    /**
     * Sets the username that will be used when connecting to the SMTP
     * server. The default is null, or no username.
     *
     * @param port the SMTP password.
     */
    public synchronized void setSMTPPassword(String password) {
        this.SMTPPassword = password;
        // JavaMail Session no longer valid so set to null; it will get
        // recreated as needed.
        SMTPSession = null;
    }

    /**
     * Returns true if debugging is turned on for the email transport layers.
     * Debug information is written to <tt>System.out</tt> by the underlying
     * JavaMail provider.
     *
     * @return true if debugging is turned on.
     */
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    /**
     * Toggles SMTP transport layer debugging on or off. Debug information is
     * written to <tt>System.out</tt> by the underlying JavaMail provider.
     *
     * @param debug true if SMTP debugging should be enabled.
     */
    public synchronized void setDebugEnabled(boolean debugEnabled) {
        this.debugEnabled = debugEnabled;
        gateway.setDebugEnabled(debugEnabled);
        // JavaMail SMTP Session no longer valid so set to null; it will get
        // recreated as needed.
        SMTPSession = null;
    }

    /**
     * Returns the body that will be used when creating temporary parent
     * messages. It's possible with email accounts and mailing
     * lists to get a response to a message before getting the original message.
     * If this happens and only the child message is available at the time of
     * the gateway import, Jive must still have a way to establish a correct
     * parent/child relationship. Therefore, a temporary fake parent message is
     * created using an extrapolated subject from the child, and a body with the
     * value <tt>temporaryParentBody</tt>. By default, this value will be
     * the empty String. However, you may wish to create a short message that
     * explains the nature of the fake parent message to the user.<p>
     *
     * On subsequent imports when a real parent message is found, the fake
     * data will be replaced with the correct subject and body.<p>
     *
     * @return the message body that will be used for temporary fake parent
     *      messages.
     */
    public String getTemporaryParentBody() {
        return gateway.getTemporaryParentBody();
    }

    /**
     * Sets the body that will be used when creating temporary parent
     * messages. It's possible with email accounts and mailing
     * lists to get a response to a message before getting the original message.
     * If this happens and only the child message is available at the time of
     * the gateway import, Jive must still have a way to establish a correct
     * parent/child relationship. Therefore, a temporary fake parent message is
     * created using an extrapolated subject from the child, and a body with the
     * value <tt>temporaryParentBody</tt>. By default, this value will be
     * the empty String. However, you may wish to create a short message that
     * explains the nature of the fake parent message to the user.<p>
     *
     * On subsequent imports when a real parent message is found, the fake
     * data will be replaced with the correct subject and body.<p>
     *
     * @param temporaryParentBody the message body that will be used for
     *      temporary fake parent messages.
     */
    public void setTemporaryParentBody(String temporaryParentBody) {
        gateway.setTemporaryParentBody(temporaryParentBody);
    }

    /**
     * Returns true if a user's privacy setting on their email address should be
     * obeyed when doing an export.
     *
     * @return true if a user's privacy settings are respected during exports.
     */
    public boolean isEmailPrefEnabled() {
        return emailPrefEnabled;
    }

    /**
     * Toggles whether a user's privacy setting on their email address should be
     * obeyed when doing an export.
     *
     * @param enabled true if a user's privacy settings are respected during
     *      exports.
     */
    public void setEmailPrefEnabled(boolean enabled) {
        this.emailPrefEnabled = enabled;
    }

    /**
     * Returns the email address that messages will be sent to during exports.
     * If the gateway is for a mailing list, this would be the mailing list
     * address (e.g., <tt>your-list@example.com</tt>).
     *
     * @return the email address that will receive exported messages.
     */
    public String getToAddress() {
        return toAddress;
    }

    /**
     * Sets the email address that messages will be sent to during exports.
     * If the gateway is for a mailing list, this would be the mailing list
     * address (e.g., <tt>your-list@example.com</tt>).
     *
     * @param address the email address that will receive exported messages.
     */
    public void setToAddress(String address) {
        if ("".equals(address)) {
            address = null;
        }
        this.toAddress = address;
    }

    public String getDefaultFromAddress() {
        return defaultFromAddress;
    }

    public void setDefaultFromAddress(String address) {
        if ("".equals(address)) {
            address = null;
        }
        this.defaultFromAddress = address;
    }

    //OTHER//

    /**
     * An extension of the JavaMailGateway class that performs imports
     * through POP3.
     */
    private class POP3Gateway extends JavaMailGateway {

        public POP3Gateway(ForumFactory factory, Forum forum) {
            super(factory, forum);
            setPort(110);
            setProtocol("pop3");
            setMailbox("INBOX"); // only choice for Sun's POP3 provider.

            gatewayMessageId = GATEWAY_MESSAGE_ID;
            gatewayParentId  = GATEWAY_PARENT_ID;
        }

        public void exportData(ForumMessage message) throws GatewayException {
            // Exporting via POP3 can't be done.
            throw new UnsupportedOperationException();
        }
    }
}